<template>
  <view class="state-view">
    <PageLayout full :page-style="pageStyle" :footer-size="footerSize" :no-footer="!currentViewConfig.actions.length">
      <template #header>
        <HeaderLayout show-back safe-capsule auto-back :show-home="control.store.programStartedByClient">
          <template #right>
            <slot name="header-right"></slot>
          </template>
        </HeaderLayout>
      </template>

      <ProgressView>
        <template #title>{{ currentViewConfig.title }}</template>

        <slot name="state-view-prefix"></slot>
        <view class="state-view-content" :style="currentViewConfig.style || {}">
          <block v-for="(content, index) in currentViewConfig.contents" :key="index">
            <view class="state-view-content-row" :style="content.style || {}">
              <view class="label">{{ content.label }}</view>
              <view class="value">
                <text class="value-text">{{ content.value }}</text>
              </view>
            </view>

            <view
              v-if="content.role === 'programName' && (hasSelectedOptions || hasAdjustableOptions)"
              class="state-view-content-row state-view-options"
              :class="{ 'has-arrow': hasAdjustableOptions }"
              @click="openAdjustOptionModal"
            >
              <view v-if="hasSelectedOptions" class="selected-options">
                <text v-for="(optionName, index) in selectedOptions" :key="index" class="option-text">{{
                  optionName
                }}</text>
              </view>
              <view v-else class="selected-options">
                <text class="option-text">{{ adjustOptionEntryName }}</text>
              </view>
            </view>
          </block>
        </view>
        <slot name="state-view-suffix"></slot>
      </ProgressView>

      <!-- 断电确认弹框 -->
      <ShutdownTips v-model:show="showShutdownTips" :control="control" @confirm="confirmShutdown" />

      <!-- 终止程序 二次确认弹框 -->
      <Modal
        :visible="showAbortDialog"
        :description="$t('Appliance.AbortProgram.Button.Text')"
        :cancel-text="$t('APP.Common.Cancel.Text')"
        :ok-text="$t('APP.Common.Confirm.Text')"
        @ok="confirmAbort"
        @cancel="dismissAbort"
      />

      <template #footer>
        <FooterLayout v-if="currentViewConfig.actions.length" fixed :size="footerSize" :footer-style="footerStyle">
          <ActionGroup :plain-disabled="currentViewConfig.actions.every((action) => action.disabled)">
            <ActionButton
              v-for="action in currentViewConfig.actions"
              :key="action.event"
              :vibrate="action.vibrate"
              :plain-disabled="action.disabled"
              @click="triggerEvent(action)"
            >
              <text>{{ action.name }}</text>
            </ActionButton>
          </ActionGroup>
        </FooterLayout>
      </template>
    </PageLayout>
  </view>
</template>

<script lang="ts">
/**
 * 视图配置
 */
// 支持的状态
type SupportedOperationStates = Exclude<
  OPERATION_STATES,
  OPERATION_STATES.READY | OPERATION_STATES.INACTIVE | OPERATION_STATES.STANDBY
>
// 支持的预设操作
type SupportedPresetActions =
  | 'pause'
  | 'abort'
  | 'abort-confirm'
  | 'resume'
  | 'cancel-order'
  | 'back-home'
  | 'acknowledge-back-home'
  | 'power-off'
// 当前行类型
type StateContentRoles = 'programName' | 'remaining' | 'orderTime' | 'finishTime'

export type StateViewConfig = {
  title: string
  contents: StateContent[]
  actions: StateAction[]
  style?: CSSProperties
}
export type StateContent = {
  label: string
  value: string
  style?: CSSProperties
  role?: StateContentRoles
}
export type StateAction = {
  name: string
  // 触发的事件名称
  event?: string
  // 触发事件时是否振动
  vibrate?: boolean
  // 是否阻止执行预设事件
  stopPreset?: boolean
  // 预设事件的触发事件类型
  presetEvent?: SupportedPresetActions
  disabled?: boolean
}

// 配置获取方法
type DefaultConfigGetter = (currentState: SupportedOperationStates) => StateViewConfig
type ContentSetters = Record<string, (config: StateViewConfig, options: { state: OPERATION_STATES }) => void>
export type CustomConfigGetter = (
  currentState: SupportedOperationStates,
  defaultGetter: DefaultConfigGetter,
  contentSetters: ContentSetters
) => StateViewConfig
type ActionSetters = Record<string, (config: StateViewConfig, options: { state: OPERATION_STATES }) => void>
</script>

<script setup lang="ts">
import type { CSSProperties } from 'vue'
import type { BaseController } from '@/controller/BaseAppliance/definition'
import PageLayout from '@/layout/page/page.vue'
import HeaderLayout from '@/layout/page/header.vue'
import FooterLayout from '@/layout/page/footer.vue'
import ProgressView from './progressView.vue'
import { footerStyle, footerSize } from '@/layout/appliance/style'
import { CONFIRM_ACKNOWLEDGE, OPERATION_STATES, PROGRAM_FINISHED } from '@/constants/Command'
import dayjs from 'dayjs'
import { useControlState } from '@/controller/BaseAppliance/useControl'
import ActionButton from '@/common/actionButton/actionButton.vue'
import ActionGroup from '@/common/actionButton/actionGroup.vue'
import { useI18n } from 'vue-i18n'
import { setCommand } from '@/api/appliance'
import { trackEvent, TrackType } from '@/dataReport/tracks'
import { safeTrack } from '@/dataReport/dataReportUtil'
import Modal from '@/common/modal.vue'
import { actionDebounce, goBackPage, toHome } from '@/utils/mpHelper'
import type { CustomProgramNameGetter } from '../components/programList.vue'
import ShutdownTips from './powerShutdown/shutdownTips.vue'
import { useShutdown } from './powerShutdown/useShutdown'
import {
  checkPauseAccessable,
  checkResumeAccessable,
  checkAbortAccessable,
  checkAdjustAccessable,
} from '@/controller/common/ErrorHandler/validator'
import { SelectOptionsObserve } from '@/controller/common/Program/program'

/**
 * Props
 */
const props = defineProps<{
  control: BaseController.Control
  customProgramNameGetter?: CustomProgramNameGetter
  customStateGetter?: CustomConfigGetter
}>()

/**
 * Emit
 */
const emit = defineEmits<{
  (e: 'action', action: StateAction): void
  (e: 'adjust'): void
}>()

const i18n = useI18n()
const pageStyle: CSSProperties = {
  backgroundColor: '#ffffff',
}

/**
 * 数据
 */
const detail = computed(() => props.control.store.detail)
// 程序名称
const programName = computed(() => {
  const programKey = props.control.store.currentProgramKey
  const programName =
    props.control.store.currentProgramName ||
    detail.value?.programName ||
    props.control.store.selectedProgramOptions?.name ||
    ''
  return props.customProgramNameGetter?.(programKey, programName, true) || programName
})
// 运行时长
const programRemaining = computed(() => props.control.store.programRemainingText)
// 程序选项
const activedOptionNames = computed(() => props.control.store.activedOptionNames)
const {
  powerDisabled,
  pauseDisabled,
  resumeDisabled,
  abortDisabled,
  pauseSupported,
  resumeSupported,
  abortSupported,
  cancelOrderDisabled,
} = useControlState(props.control)

/**
 * 运行中调节参数
 */
const selectedOptions = computed<string[]>(() => {
  const selectionOptionNames: string[] = []
  const checkedOptionNames: string[] = []

  if (!activedOptionNames.value.length) return []

  for (const activedOption of activedOptionNames.value) {
    if (activedOption.label && activedOption.value) {
      selectionOptionNames.push(`${activedOption.label}: ${activedOption.value}`)
    } else {
      // 兼容只有label或value的情况
      let displayOptionLabel = activedOption.label
      if (displayOptionLabel === '') {
        displayOptionLabel = activedOption.value !== void 0 ? activedOption.value : ''
      }

      displayOptionLabel !== '' && checkedOptionNames.push(displayOptionLabel)
    }
  }

  return selectionOptionNames.concat(checkedOptionNames)
})
// 可调节参数
const adjustableOptions = computed(() => props.control.store.adjustableOptions)
// 存在可调节参数
const hasAdjustableOptions = computed(() => adjustableOptions.value.length !== 0)
// 存在已选中参数
const hasSelectedOptions = computed(() => selectedOptions.value.length !== 0)
// 调节参数入口名称
const adjustOptionEntryName = computed(
  () => props.control.store.adjustParameterTitle || i18n.t('Appliance.AdjustOption.Button.Text')
)
// 调节参数弹框
function openAdjustOptionModal() {
  if (!hasAdjustableOptions.value) return

  // 调接口前检查
  const accessable = checkAdjustAccessable(props.control)
  if (!accessable) return

  safeTrack(() => {
    trackEvent('Appliance.AdjustOption.Button.Text', {}, TrackType.ApplianceDetail)
  })

  emit('adjust')
}

onBeforeMount(() => {
  props.control.remote.addBatchObserve(SelectOptionsObserve)
})
onBeforeUnmount(() => {
  props.control.remote.removeBatchObserve(SelectOptionsObserve)
})

/**
 * 视图配置
 */
// 设置显示内容方法
const contentSetters: ContentSetters = {
  putProgramName: (config) => {
    programName.value &&
      config.contents.push({
        label: i18n.t('Appliance.StateView.Label.ProgramName.Text'),
        value: programName.value,
        role: 'programName',
      })
  },
  putProgramRemaining: (config, options) =>
    programRemaining.value &&
    config.contents.push({
      label:
        options.state === OPERATION_STATES.DELAYED_START
          ? i18n.t('Appliance.StateView.Label.ProgramRemaining.DelayedStart.Text')
          : i18n.t('Appliance.StateView.Label.ProgramRemaining.Text'),
      value: programRemaining.value,
    }),
  putEstimatedFinishTime: (config) => {
    const finishTime = getFinishTime()
    finishTime &&
      config.contents.push({
        label: i18n.t('Appliance.StateView.Label.EstimatedFinishTime.Text'),
        value: finishTime,
      })
  },
  putOrderTime: (config) => {
    const orderTime = getOrderTimeText() // 运行时间
    orderTime && config.contents.push({ label: i18n.t('Appliance.StateView.Label.OrderTime.Text'), value: orderTime })
  },
}

// 设置显示按钮方法
const actionSetters: ActionSetters = {
  putPowerOff: (config) => {
    const powerStateAvailable = props.control.store.powerState !== null
    powerStateAvailable &&
      config.actions.push({
        name: i18n.t('Appliance.StateView.PowerOff.Button.Text'),
        disabled: powerDisabled.value,
        presetEvent: 'power-off',
      })
  },
  putBackHome: (config) => {
    config.actions.push({ name: i18n.t('Appliance.StateView.BackHome.Button.Text'), presetEvent: 'back-home' })
  },
  putAcknowledgeBackHome: (config) => {
    config.actions.push({
      name: i18n.t('Appliance.StateView.AcknowledgeBackHome.Button.Text'),
      presetEvent: 'acknowledge-back-home',
    })
  },
}

// 默认视图状态显示配置
const defaultConfigGetter: DefaultConfigGetter = (currentState: SupportedOperationStates) => {
  switch (currentState) {
    case OPERATION_STATES.RUN: {
      const config: StateViewConfig = {
        title: i18n.t('Appliance.StateView.Run.Title'),
        contents: [],
        actions: [
          {
            name: i18n.t('Appliance.StateView.Abort.Button.Text'),
            presetEvent: 'abort-confirm',
            disabled: abortDisabled.value || !abortSupported.value,
          },
        ],
      }
      /**
       * contents
       */
      contentSetters.putProgramName(config, { state: currentState })
      contentSetters.putProgramRemaining(config, { state: currentState })
      contentSetters.putEstimatedFinishTime(config, { state: currentState })
      /**
       * actions
       */
      if (!props.control.store.hidePauseForced) {
        config.actions.unshift({
          name: i18n.t('Appliance.StateView.Pause.Button.Text'),
          presetEvent: 'pause',
          disabled: pauseDisabled.value || !pauseSupported.value,
        })
      }
      return config
    }
    case OPERATION_STATES.DELAYED_START: {
      const config: StateViewConfig = {
        title: i18n.t('Appliance.StateView.DelayStart.Title'),
        contents: [],
        actions: [
          {
            name: i18n.t('Appliance.StateView.CancelOrder.Button.Text'),
            presetEvent: 'cancel-order',
            disabled: cancelOrderDisabled.value,
          },
        ],
      }
      /**
       * contents
       */
      contentSetters.putProgramName(config, { state: currentState })
      contentSetters.putProgramRemaining(config, { state: currentState })
      contentSetters.putOrderTime(config, { state: currentState })
      return config
    }
    case OPERATION_STATES.PAUSE: {
      const config: StateViewConfig = {
        title: i18n.t('Appliance.StateView.Pause.Title'),
        contents: [],
        actions: [
          {
            name: i18n.t('Appliance.StateView.Resume.Button.Text'),
            presetEvent: 'resume',
            disabled: resumeDisabled.value || !resumeSupported.value,
          },
          {
            name: i18n.t('Appliance.StateView.Abort.Button.Text'),
            presetEvent: 'abort-confirm',
            disabled: abortDisabled.value || !abortSupported.value,
          },
        ],
      }
      /**
       * contents
       */
      contentSetters.putProgramName(config, { state: currentState })
      contentSetters.putProgramRemaining(config, { state: currentState })
      contentSetters.putEstimatedFinishTime(config, { state: currentState })
      return config
    }
    case OPERATION_STATES.ABORTING: {
      const config: StateViewConfig = {
        title: i18n.t('Appliance.StateView.Aborting.Title'),
        contents: [],
        actions: [{ name: i18n.t('Appliance.StateView.Aborting.Button.Text'), disabled: true }],
        style: {
          color: 'rgba(var(--dark-vector), 0.3)',
        },
      }
      /**
       * contents
       */
      contentSetters.putProgramName(config, { state: currentState })
      return config
    }
    case OPERATION_STATES.FINISHED: {
      const config: StateViewConfig = {
        title: i18n.t('Appliance.StateView.Finished.Title'),
        contents: [{ label: i18n.t('Appliance.StateView.Finished.Content'), value: '' }],
        actions: [],
      }
      actionSetters.putPowerOff(config, { state: currentState })
      actionSetters.putBackHome(config, { state: currentState })
      return config
    }
    case OPERATION_STATES.PROGRAM_FINISHED: {
      const config: StateViewConfig = {
        title: i18n.t('Appliance.StateView.ProgramFinished.Title'),
        contents: [{ label: i18n.t('Appliance.StateView.ProgramFinished.Content'), value: '' }],
        actions: [],
      }
      actionSetters.putPowerOff(config, { state: currentState })
      actionSetters.putAcknowledgeBackHome(config, { state: currentState })
      return config
    }
    case OPERATION_STATES.ACTION_REQUIRED: {
      return {
        title: i18n.t('Appliance.StateView.ActionRequired.Title'),
        contents: [{ label: i18n.t('Appliance.StateView.ActionRequired.Content'), value: '' }],
        actions: [],
      }
    }
    case OPERATION_STATES.ERROR: {
      return {
        title: i18n.t('Appliance.StateView.Error.Title'),
        contents: [],
        actions: [],
      }
    }
    default: {
      return {
        title: '',
        contents: [],
        actions: [],
      }
    }
  }
}
const currentViewConfig = computed<StateViewConfig>(() => {
  const operationState = props.control.store.operationState as SupportedOperationStates
  // 找不到配置时
  if (!operationState || !props.customStateGetter) {
    return defaultConfigGetter(operationState)
  }

  // 自定义覆盖默认
  return props.customStateGetter(operationState, defaultConfigGetter, contentSetters)
})

// 获取结束时间
function getFinishTime() {
  let endTime = detail.value?.displayTimes?.finishInAbsolute || ''
  if (endTime) {
    endTime = dayjs(endTime).format('HH:mm')
  }
  return endTime
}

// 获取预约时间
function getOrderTimeText() {
  let startTime = detail.value?.displayTimes?.startInAbsolute || ''
  let endTime = detail.value?.displayTimes?.finishInAbsolute || ''
  if (startTime && endTime) {
    startTime = dayjs(startTime).format('HH:mm')
    endTime = dayjs(endTime).format('HH:mm')
    return `${startTime} - ${endTime}`
  }
  return ''
}

/**
 * 事件处理
 */
function triggerEvent(action: StateAction) {
  const { event, presetEvent } = action

  event && emit('action', action)

  // 未配置预设 或 阻止预设事件
  if (!presetEvent || action.stopPreset) return
  switch (presetEvent) {
    case 'abort-confirm': {
      if (!checkAbortAccessable(props.control)) return

      return showAbortConfirm()
    }
    case 'abort': {
      if (!checkAbortAccessable(props.control)) return
      return abortProgram()
    }
    case 'cancel-order': {
      let trackData = {
        program_key: props.control.store.currentProgramKey || '',
        program_name: props.control.store.currentProgramName || '',
        option_obj_1: {
          run_time: programRemaining.value,
          run_time_period: getOrderTimeText(),
        },
      }
      return props.control.store.abortProgram(trackData, true) // 取消预约 复用终止程序方法
    }
    case 'pause': {
      if (!checkPauseAccessable(props.control)) return
      let trackData = trackCommon('pause_program', 'Appliance.StateView.Pause.Button.Text')
      return props.control.store.pauseProgram(trackData)
    }
    case 'resume': {
      if (!checkResumeAccessable(props.control)) return
      let trackData = trackCommon('resume_program', 'Appliance.StateView.Resume.Button.Text')
      return props.control.store.resumeProgram(trackData)
    }
    case 'back-home': {
      return backHome()
    }
    case 'acknowledge-back-home': {
      return acknowledgeBackHome()
    }
    case 'power-off': {
      return onOff()
    }
  }
}
// 停止 暂停 继续 埋点
function trackCommon(event: string, buttonId: string) {
  return {
    program_key: props.control.store.currentProgramKey || '',
    program_name: props.control.store.currentProgramName || '',
    option_obj_1: {
      end_time: getFinishTime(),
      run_time: programRemaining.value,
      start_time: props.control.store.programStartTime,
    },
  }
}

/**
 * 终止程序
 */
const showAbortDialog = ref(false)
function showAbortConfirm() {
  showAbortDialog.value = true
}
function confirmAbort() {
  abortProgram()
  dismissAbort()
}
function dismissAbort() {
  showAbortDialog.value = false
}
function abortProgram() {
  let trackData = trackCommon('stop_program', 'Appliance.StateView.Abort.Button.Text')
  return props.control.store.abortProgram(trackData)
}

/**
 * 回到首页
 */
const backHome = actionDebounce({
  call: async () => {
    try {
      await goBackPage()
      // 返回首页 重置虚拟家电的运行状态
      if (props.control.store.isVirtualAppliance) {
        props.control.store.operationState = OPERATION_STATES.READY
      }
    } catch (e) {
      console.error(e)
      await toHome()
    }
  },
})

const acknowledgeBackHome = actionDebounce({
  call: async () => {
    try {
      const haId = props.control.store._datasource.haId
      await setCommand(haId, CONFIRM_ACKNOWLEDGE, PROGRAM_FINISHED)
      await backHome()
    } catch (e) {
      console.error(e)
    }
  },
})

/**
 * 开关机
 */
const { showShutdownTips, onPowerChange, confirmShutdown } = useShutdown(props.control)
function onOff() {
  onPowerChange()
}
</script>

<style scoped lang="less">
.state-view-content-row {
  display: flex;
  + .state-view-content-row {
    margin-top: 16rpx;
  }
  .label {
    min-width: 144rpx;
    flex-shrink: 0;
  }
  .label + .value {
    margin-left: 24rpx;
  }
  .value {
    display: flex;
    flex-wrap: wrap;
  }
  .value-text {
    white-space: wrap;
    margin-right: 48rpx;
  }
}

.state-view-options {
  background-color: #f4f4f6;
  padding: 24rpx;
  position: relative;
  &.has-arrow {
    padding-right: 48rpx;
    &::after {
      content: '';
      display: block;
      width: 16rpx;
      height: 28rpx;
      background-repeat: no-repeat;
      background-size: 100% 100%;
      background-position: center center;
      background-image: url('data:image/svg+xml;charset=utf-8,%3Csvg%20width%3D%228%22%20height%3D%2214%22%20viewBox%3D%220%200%208%2014%22%20fill%3D%22none%22%20xmlns%3D%22http%3A%2F%2Fwww.w3.org%2F2000%2Fsvg%22%3E%0A%3Cpath%20opacity%3D%220.32%22%20d%3D%22M8%207C8%206.79839%207.9182%206.6129%207.76278%206.46774L1.28425%200.209677C1.13701%200.0725806%200.957055%200%200.744376%200C0.327198%200%200%200.314516%200%200.733871C0%200.935484%200.0817996%201.12097%200.212679%201.25806L6.16769%207L0.212679%2012.7419C0.0817996%2012.879%200%2013.0565%200%2013.2661C0%2013.6855%200.327198%2014%200.744376%2014C0.957055%2014%201.13701%2013.9274%201.28425%2013.7823L7.76278%207.53226C7.9182%207.37903%208%207.20161%208%207Z%22%20fill%3D%22%232D3746%22%2F%3E%0A%3C%2Fsvg%3E');
      position: absolute;
      top: 50%;
      right: 24rpx;
      transform: translateY(-50%);
    }
  }
  .option-text {
    margin-right: 48rpx;
    margin-bottom: 24rpx;
  }
}
</style>
